/**
  Copyright (c) [2024] [JiangNan]
  [go-tools] is licensed under Mulan PSL v2.
  You can use this software according to the terms and conditions of the Mulan PSL v2.
  You may obtain a copy of Mulan PSL v2 at:
           http://license.coscl.org.cn/MulanPSL2
  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
  See the Mulan PSL v2 for more details.
*/

/*
	data 为数据处理/转化的简单包
*/

package data

import (
	"cmp"
	"errors"
	"fmt"
	"gitee.com/jn-qq/go-tools/slices"
	"math"
	"regexp"
	"strconv"
	"strings"
	"unicode"
)

// Int2AAA 将表格列索引（int）转换为列名(string)
func Int2AAA(col, row int) (string, error) {
	if col < 1 || row < 1 {
		return "", errors.New("row number exceeds minimum limit")
	}
	if row > 1048576 {
		return "", errors.New("row number exceeds maximum limit")
	} else if col > 16384 {
		return "", fmt.Errorf("the column number must be greater than or equal to 1 and less than or equal to 16384")
	}

	estimatedLength := 0
	for n := col; n > 0; n = (n - 1) / 26 {
		estimatedLength++
	}

	result := make([]byte, estimatedLength)
	for col > 0 {
		estimatedLength--
		result[estimatedLength] = byte((col-1)%26 + 'A')
		col = (col - 1) / 26
	}

	return string(result) + strconv.Itoa(row), nil
}

// AAA2Int 将表格列名(string)转换为列索引（int）
func AAA2Int(colName string) (colIndex int) {
	length := len(colName)
	for i, col := range colName {
		colIndex += int(col-64) * int(math.Pow(26, float64(length-1-i)))
	}
	return
}

var numberCn2an = map[string]int{
	"零": 0,
	"〇": 0,
	"一": 1,
	"壹": 1,
	"幺": 1,
	"二": 2,
	"贰": 2,
	"两": 2,
	"三": 3,
	"叁": 3,
	"四": 4,
	"肆": 4,
	"五": 5,
	"伍": 5,
	"六": 6,
	"陆": 6,
	"七": 7,
	"柒": 7,
	"八": 8,
	"捌": 8,
	"九": 9,
	"玖": 9,
}

var unitCn2an = map[string]int{
	"十": 10,
	"拾": 10,
	"百": 100,
	"佰": 100,
	"千": 1000,
	"仟": 1000,
	"万": 10000,
	"亿": 100000000,
}
var numberAn2cn = []string{"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"}
var unitAn2cn = []string{"", "十", "百", "千", "万", "十万", "百万", "千万", "亿", "十亿", "百亿", "千亿", "万亿", "亿亿"}

// Cn2an 将中文数字转换为阿拉伯数字，不支持小数
func Cn2an(cn string) (int, error) {

	var out int
	var cnList []string

	for _, b := range cn {
		slices.Insert(cnList, 0, string(b))
	}

	if _, ok := unitCn2an[cnList[len(cnList)-1]]; ok {
		cnList = append(cnList, "一")
	}

	weight := 1
	big := false
	for i, c := range cnList {
		if a, ok := numberCn2an[c]; ok {
			out += a * weight
			weight *= 10
			big = false
		} else if a, ok := unitCn2an[c]; ok {
			// 保权
			if big {
				weight *= a
			} else {
				weight = a
			}
			big = true
		} else {
			return 0, fmt.Errorf("第%d个字符不是中文数字", i)
		}
	}

	return out, nil
}

// An2cn 阿拉伯数字转汉字
func An2cn(an int) (string, error) {

	var out string

	str := strconv.Itoa(an)
	for i, a := range str {
		if string(a) == "0" {
			out += "零"
			continue
		}
		if index := len(str) - 1 - i; index < len(unitAn2cn) {
			a_n, _ := strconv.Atoi(string(a))
			out += numberAn2cn[a_n] + unitAn2cn[index]
		} else {
			return "", fmt.Errorf("数字太大了")
		}
	}
	// 去重 “零”
	re := regexp.MustCompile(`零{2,}`)
	out = strings.Trim(re.ReplaceAllString(out, "零"), "零")

	return out, nil
}

// IsDigit 判断字符串是否为全数字
func IsDigit(s string) bool {
	for _, r := range s {
		if !unicode.IsDigit(r) {
			return false
		}
	}
	return true
}

// HanCount 计算字符串中 汉字个数
func HanCount(str string) (count int) {
	for _, char := range str {
		if unicode.Is(unicode.Han, char) {
			count++
		}
	}
	return
}

// TernaryExpression 三元表达式
func TernaryExpression[T any](condition bool, t, y T) T {
	if condition {
		return t
	} else {
		return y
	}
}

func ToString(input any) (string, error) {
	switch v := input.(type) {
	case string:
		return v, nil
	case int:
		return strconv.Itoa(v), nil
	case int64:
		return strconv.FormatInt(v, 10), nil
	case int32:
		return strconv.FormatInt(int64(v), 10), nil
	case int16:
		return strconv.FormatInt(int64(v), 10), nil
	case int8:
		return strconv.FormatInt(int64(v), 10), nil
	case float64:
		return strconv.FormatFloat(v, 'g', -1, 64), nil // 使用 'g' 格式以支持科学计数法
	case float32:
		return strconv.FormatFloat(float64(v), 'g', -1, 32), nil
	case bool:
		return strconv.FormatBool(v), nil // 简化布尔值处理
	default:
		// 返回更具描述性的默认值或记录日志
		return "", fmt.Errorf("<Unsupported type: %T>", v)
	}
}

func SliceToString[T cmp.Ordered | bool](s []T) ([]string, error) {
	var result []string
	for _, t := range s {
		toString, err := ToString(t)
		if err != nil {
			return nil, err
		}
		result = append(result, toString)
	}
	return result, nil
}
